package com.secsbrain.deploy.biz.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import com.secsbrain.cmdb.client.iface.AppInfoService;
import com.secsbrain.cmdb.client.iface.HostInfoService;
import com.secsbrain.deploy.biz.utils.HttpResponse;
import com.secsbrain.deploy.biz.IBuildRecordService;
import com.secsbrain.deploy.biz.utils.JenkinsHttpUtils;
import com.secsbrain.deploy.client.constant.EnumDefinition;
import com.secsbrain.deploy.client.model.BuildParam;
import com.secsbrain.deploy.client.model.HealthDto;
import com.secsbrain.deploy.dal.mapper.BuildModelMapper;
import com.secsbrain.deploy.dal.mapper.BuildRecordMapper;
import com.secsbrain.deploy.dal.model.BuildModel;
import com.secsbrain.deploy.dal.model.BuildRecord;
import com.secsbrain.deploy.remote.utils.HttpUtils;
import com.secsbrain.deploy.utils.command.CommandRunner;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.secsbrain.deploy.utils.command.WriteToFileStrategy;

import com.secsbrain.cmdb.client.model.AppDetail;
import com.secsbrain.cmdb.client.model.Server;
import com.secsbrain.cmdb.client.model.SystemTypeEnums;
import com.secsbrain.common.ResponseVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.framework.service.impl.SuperServiceImpl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;

/**
 * BuildRecord 表数据服务层接口实现类
 */
@Service
public class BuildRecordServiceImpl extends SuperServiceImpl<BuildRecordMapper, BuildRecord> implements IBuildRecordService {

    private static final Logger logger = LoggerFactory.getLogger(BuildRecordServiceImpl.class);

    @Autowired
    AppInfoService appInfoService;

    @Autowired
    HostInfoService hostInfoService;

    @Autowired
    HttpUtils httpUtils;

    @Resource
    private CommandRunner commandRunner;

    @Value("${jenkins.url}")
    private String jenkinsUrl;

    @Value("${jenkins.userid}")
    private String userid;

    @Value("${jenkins.token}")
    private String token;

    @Value("${jenkins.k8s.url}")
    private String jenkinsK8sUrl;

    @Value("${jenkins.k8s.userid}")
    private String useridK8s;

    @Value("${jenkins.k8s.token}")
    private String tokenK8s;

    @Autowired
    BuildModelMapper buildModelMapper;

    @Autowired
    BuildRecordMapper buildRecordMapper;

    /* (non-Javadoc)
     * @see IBuildRecordService#insertBuildRecord(com.secsbrain.cmdb.client.model.App, BuildParam)
     */
    @Override
    @Transactional
    public BuildRecord insertBuildRecord(AppDetail app, BuildParam buildParam, int status) {
        BuildRecord buildRecord = new BuildRecord();
        buildRecord.setAppId(buildParam.getAppId());
        buildRecord.setBranch(buildParam.getBranchName());
        buildRecord.setCreateTime(new Date());
        buildRecord.setEnv(buildParam.getEnvName());
        buildRecord.setNameSpace(app.getNameSpace());
        buildRecord.setStatus(status);
        buildRecord.setApprovalId(buildParam.getApprovalId());
        buildRecord.setCreateName(buildParam.getBuildUserName());
        buildRecord.setPackageCmd(buildParam.isPackage() ? 1 : 0);
        buildRecord.setDeployCmd(buildParam.isDeploy() ? 1 : 0);
        buildRecord.setTestCmd(buildParam.isTest() ? 1 : 0);
        buildRecord.setGroupName(buildParam.getGroupName());
        buildRecord.setGroupId(buildParam.getGroupId());
        buildRecord.setBatchId(buildParam.getBatchId());
        buildRecord.setHostNames(getHostNames(buildParam, app.getServerList()));
        buildRecord.setAppName(Server.K8S_HOSTNAME.equals(buildRecord.getHostNames()) ? app.getName() + "-" + Server.K8S_HOSTNAME : app.getName());
        this.insertSelective(buildRecord);

        if (buildParam.getGroupId() == null) {
            buildRecord.setGroupId(buildRecord.getId());
            this.updateSelectiveById(buildRecord);
        }
        return buildRecord;
    }

    /**
     * 功能描述: <br>
     * 获得hostnames，区分是否分组的情况
     *
     * @param buildParam
     * @param serverList
     * @return
     */
    @SuppressWarnings("rawtypes")
    private String getHostNames(BuildParam buildParam, List<Server> serverList) {
        if (buildParam != null && StringUtils.isNotEmpty(buildParam.getGroupName())) {
            BuildModel buildModelParam = new BuildModel();
            buildModelParam.setAppId(buildParam.getAppId());
            buildModelParam.setEnv(buildParam.getEnvName());
            BuildModel buildModel = buildModelMapper.selectOne(buildModelParam);
            Map modelMap = JSON.parseObject(buildModel.getModelJson(), Map.class);
            Map<String, String> groupServerIp = getGroupServerIp((String) modelMap.get("switchEnvName"), serverList);
            return groupServerIp.get(buildParam.getGroupName());
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            for (Server server : serverList) {
                stringBuilder.append(server.getHostName()).append(",");
            }
            return stringBuilder.deleteCharAt(stringBuilder.length() - 1).toString();
        }
    }

    /**
     * 功能描述: <br>
     * 获取jvm参数
     *
     * @param buildParam
     * @param env
     * @return
     */
    @SuppressWarnings("rawtypes")
    private String getJvmOps(BuildParam buildParam, String env) {
        BuildModel buildModelParam = new BuildModel();
        buildModelParam.setAppId(buildParam.getAppId());
        buildModelParam.setEnv(buildParam.getEnvName());
        BuildModel buildModel = buildModelMapper.selectOne(buildModelParam);
        Map modelMap = JSON.parseObject(buildModel.getModelJson(), Map.class);
        if (!modelMap.containsKey("jvmOpsMap")) {
            return "";
        }
        Map jvmOpsMap = (Map) modelMap.get("jvmOpsMap");
        if (!jvmOpsMap.containsKey(env)) {
            return "";
        }
        return (String) jvmOpsMap.get(env);
    }

    /* (non-Javadoc)
     * @see IBuildRecordService#getGroupServerIp(java.lang.String, java.util.List)
     */
    public Map<String, String> getGroupServerIp(String switchEnvName, List<Server> serverList) {
        List<Server> switchServerList = new ArrayList<Server>();
        for (Server server : serverList) {
            if (switchEnvName.equals(server.getEnvName())) {
                switchServerList.add(server);
            }
        }
        if (switchServerList.size() <= 1) {
            throw new RuntimeException("切流/分组环境：" + switchEnvName + "的服务器至少需要两台！");
        }
        int groupA = 0;
        int groupB = 0;
        StringBuffer bufferA = new StringBuffer();
        StringBuffer bufferB = new StringBuffer();
        for (Server server : switchServerList) {
            if (StringUtils.isNotEmpty(server.getGroupName()) && ("A".equals(server.getGroupName()) || "B".equals(server.getGroupName()))) {
                if ("A".equals(server.getGroupName())) {
                    bufferA.append(server.getHostName()).append(",");
                } else if ("B".equals(server.getGroupName())) {
                    bufferB.append(server.getHostName()).append(",");
                }
            } else {
                if (groupA > groupB) {
                    bufferB.append(server.getHostName()).append(",");
                    groupB++;
                } else {
                    bufferA.append(server.getHostName()).append(",");
                    groupA++;
                }
            }

        }

        if (bufferA.length() == 0) {
            return this.getNewStringBuffer(bufferA, bufferB);
        }
        if (bufferB.length() == 0) {
            return this.getNewStringBuffer(bufferB, bufferA);
        }

        bufferA.deleteCharAt(bufferA.length() - 1);
        bufferB.deleteCharAt(bufferB.length() - 1);
        Map<String, String> map = new HashMap<String, String>();
        map.put("A", bufferA.toString());
        map.put("B", bufferB.toString());
        return map;
    }

    private Map<String, String> getNewStringBuffer(StringBuffer bufferA, StringBuffer bufferB) {
        String[] bServerArray = bufferB.toString().split(",");
        for (int i = 0; i < bServerArray.length / 2; i++) {
            bufferA.append(bServerArray[i]).append(",");
        }

        StringBuilder newBufferB = new StringBuilder();
        for (int i = bServerArray.length / 2; i < bServerArray.length; i++) {
            newBufferB.append(bServerArray[i] + ",");
        }
        Map<String, String> map = new HashMap<String, String>();

        map.put("A", bufferA.deleteCharAt(bufferA.length() - 1).toString());
        map.put("B", newBufferB.deleteCharAt(newBufferB.length() - 1).toString());
        return map;
    }

    /* (non-Javadoc)
     * @see IBuildRecordService#executeBuild(com.secsbrain.cmdb.client.model.App)
     */
    @Override
    @Transactional
    public long executeBuild(AppDetail app, BuildParam buildParam) {
        BuildRecord buildRecordRes;

        if (buildParam.getBuildId() == null) {
            buildRecordRes = this.insertBuildRecord(app, buildParam, EnumDefinition.BuildStatus.WAIT.getCode());
        } else {
            BuildRecord buildRecordUp = new BuildRecord();
            buildRecordUp.setId(buildParam.getBuildId());
            buildRecordUp.setCreateTime(new Date());
            buildRecordUp.setStatus(EnumDefinition.BuildStatus.WAIT.getCode());
            this.updateSelectiveById(buildRecordUp);

            buildRecordRes = this.selectById(buildParam.getBuildId());
        }

        //TODO 如果是切流发布  ，发布之前验证其切流配置是否成功

        ResponseVo<AppDetail> responseVo = appInfoService.queryAppDetailById(buildRecordRes.getAppId());
        Assert.isTrue(responseVo.isSuccess(), responseVo.getMsg());

        AppDetail appDetail = responseVo.getData();

        //jenkins发布必选参数，gitUrl在创建job时就配置好
        Map<String, String> map = new HashMap<>();

        if ("B".equals(buildRecordRes.getGroupName())) {
//			if(SystemTypeEnums.DOTNET.toString().equals(appDetail.getSystemType())){
//				handleGrayRelease(100, buildRecordRes.getGroupId());//A组完全放流，B组关流量
//				map.put("fullrelease", "true");//B组完全放流
//			}else
            if (SystemTypeEnums.SPRING_BOOT.toString().equals(appDetail.getSystemType())) {
                handleBootGroup(buildParam, appDetail);
            }
        } else if ("A".equals(buildRecordRes.getGroupName())) {
            map.put("splitrequests", "true");//A组关流量
        }

        if ("prod".equals(buildRecordRes.getEnv())) {
            map.put("accesshosts", "true");//模拟一次请求访问线上环境，解决第一次访问慢的问题
        }

        map.put("buildId", buildRecordRes.getId() + "");
        map.put("branch", buildRecordRes.getBranch());

//	  	if("A".equals(buildRecordRes.getGroupName())&&SystemTypeEnums.DOTNET.toString().equals(appDetail.getSystemType())){
//	  		//如果是dotnet系统且是灰度，则将环境设置为gray
//    	  	map.put("env", "gray");
//        }else{
        map.put("env", buildParam.getEnvName());
//        }
        map.put("nameSpace", buildRecordRes.getNameSpace());
        map.put("hostNames", buildRecordRes.getHostNames());
        map.put("hostIps", this.getHostIps(buildRecordRes.getHostNames()));
        map.put("allHostNames", getHostNames(null, getServerListByEnv(buildParam.getEnvName(), appDetail)));
        map.put("package", buildRecordRes.getPackageCmd() == 0 ? "false" : "true");
        //maven 打包命令
        map.put("mavenCmd", buildRecordRes.getPackageCmd() == 0 ? "dependency:tree" : "clean install -Dmaven.test.skip=true");

        String jvmOps = this.getJvmOps(buildParam, buildRecordRes.getEnv());
        if (StringUtils.isNotEmpty(jvmOps)) {
            map.put("jvmOps", jvmOps);
        }

        map.put("deploy", buildRecordRes.getDeployCmd() == 0 ? "false" : "true");

        JenkinsHttpUtils jenkinsHttpUtils = null;
        if (Server.K8S_HOSTNAME.equals(buildRecordRes.getHostNames())) {
            jenkinsHttpUtils = new JenkinsHttpUtils(jenkinsK8sUrl, useridK8s, tokenK8s);
        } else {
            jenkinsHttpUtils = new JenkinsHttpUtils(jenkinsUrl, userid, token);
        }

        HttpResponse httpResponse = jenkinsHttpUtils.submitPost("/job/" + buildRecordRes.getAppName() + "/buildWithParameters", map);
        String result = httpResponse.getResult();
        if (result != null) {
            logger.error(result);
            //如果jenkins有返回，说明执行失败了
            throw new RuntimeException(result);
        }

        return buildRecordRes.getId();
    }

    /**
     * 功能描述: <br>
     * 根据hostname获取ip
     *
     * @param hostNames 如10.0.11.57,10.0.11.58
     * @return
     */
    private String getHostIps(String hostNames) {
        String[] hostNameArray = hostNames.split(",");
        List<String> ipList = this.getIpList(Arrays.asList(hostNameArray));
        return StringUtils.join(ipList, ",");
    }

    private List<Server> getServerListByEnv(String envName, AppDetail appDetail) {
        List<Server> newList = new ArrayList<Server>();
        List<Server> serverList = appDetail.getServerList();
        for (Server server : serverList) {
            if (envName != null && envName.equals(server.getEnvName())) {
                newList.add(server);
            }
        }
        return newList;
    }

    /**
     * 功能描述: <br>
     * springboot分组发布处理
     */
    private void handleBootGroup(BuildParam buildParam, AppDetail appDetail) {
        buildParam.setGroupName("A");
        String hostNames = this.getHostNames(buildParam, getServerListByEnv(buildParam.getEnvName(), appDetail));
        String[] hostNameArray = hostNames.split(",");
        List<String> hostList = Arrays.asList(hostNameArray);
        boolean flag;
        try {
            HealthDto healthDto=new HealthDto();
            healthDto.setAppId(appDetail.getId());
            healthDto.setHostList(hostList);
            healthDto.setEnv(buildParam.getEnvName());
            flag = this.healthCheck(healthDto);
        } catch (Exception e) {
            flag = false;
        }
        if (!flag) {
            throw new RuntimeException("A组健康检查未通过");
        }
    }

    /**
     * 功能描述: <br>
     * 灰度切流处理
     *
     * @param finalPercent
     * @param groupId
     */
    @Transactional
    public int handleGrayRelease(int finalPercent, Long groupId) {
        BuildRecord buildRecordParam = new BuildRecord();
        buildRecordParam.setGroupId(groupId);
        buildRecordParam.setDelStatus(0);
        EntityWrapper<BuildRecord> entityWrapper = new EntityWrapper<BuildRecord>(buildRecordParam);
        List<BuildRecord> buildSnapshotList = buildRecordMapper.selectList(entityWrapper);
        if (CollectionUtils.isEmpty(buildSnapshotList)) {
            throw new RuntimeException("无效的发布记录");
        }

        if (finalPercent < 0 || finalPercent > 100) {
            throw new RuntimeException("百分比不正确！");
        }

        BuildRecord buildRecordUp = new BuildRecord();
        buildRecordUp.setId(buildSnapshotList.get(0).getId());
        buildRecordUp.setGrayPercent(finalPercent);
        buildRecordMapper.updateSelectiveById(buildRecordUp);

        Map<String, String> params = new HashMap<>();
        params.put("appName", buildSnapshotList.get(0).getAppName());
        params.put("percent", finalPercent + "");

        String command = "/opt/scripts/grayRelease.sh " + params.get("appName") + " " + params.get("percent");
        WriteToFileStrategy writeToFileStrategy = new WriteToFileStrategy("/home/logs/grayRelease", command, buildRecordUp.getId());
        try {
            commandRunner.exec(command, writeToFileStrategy);
        } catch (IOException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }

        return finalPercent;
    }

    /* (non-Javadoc)
     * @see IBuildRecordService#queryBuildRecordPageList(com.baomidou.mybatisplus.plugins.Page, BuildRecord, java.lang.String)
     */
    @Override
    public Page<BuildRecord> queryBuildRecordPageList(Page<BuildRecord> page, BuildRecord buildRecord,
                                                      String orderByField) {
        List<BuildRecord> list = buildRecordMapper.queryBuildRecordList(page, buildRecord, orderByField);
        page.setRecords(list);
        return page;
    }

    /* (non-Javadoc)
     * @see IBuildRecordService#healthCheck(java.lang.Long, java.util.List)
     */
    @SuppressWarnings("rawtypes")
    @Override
    public boolean healthCheck(HealthDto healthDto) {
        Long appId=healthDto.getAppId();
        if (appId == null) {
            throw new RuntimeException("appId不能为空");
        }
        List<String> hostList=healthDto.getHostList();
        if (CollectionUtils.isEmpty(hostList)) {
            throw new RuntimeException("主机地址不能为空");
        }

        String env=healthDto.getEnv();
        if (StringUtils.isEmpty(env)) {
            throw new RuntimeException("环境不能为空");
        }
        logger.debug("健康检查开始。。。参数:{}", JSON.toJSONString(healthDto));

        BuildModel buildModelParam = new BuildModel();
        buildModelParam.setAppId(appId);
        buildModelParam.setEnv(healthDto.getEnv());
        BuildModel buildModel = buildModelMapper.selectOne(buildModelParam);
        Map modelMap = JSON.parseObject(buildModel.getModelJson(), Map.class);
        if (!modelMap.containsKey("port")) {
            throw new RuntimeException("应用端口不能为空");
        }
        Integer port = (Integer) modelMap.get("port");

        for (String host : this.getIpList(hostList)) {
            try {
                if (getJsonResult(host, port, null)) {
                    return true;
                } else {
                    ResponseVo<AppDetail> responseVo = appInfoService.queryAppDetailById(appId);
                    if (!responseVo.isSuccess() || responseVo.getData() == null) {
                        return false;
                    }
                    return getJsonResult(host, port, responseVo.getData().getName());
                }
            } catch (Exception e) {
                logger.warn("", e);
                return false;
            }
        }
        return true;
    }

    @SuppressWarnings("rawtypes")
    private boolean getJsonResult(String host, Integer port, String contextPath) throws HttpException, IOException {
        String healthUrl = "http://" + host + ":" + port + "/health";
        if (StringUtils.isNotEmpty(contextPath)) {
            healthUrl = "http://" + host + ":" + port + "/" + contextPath + "/health";
        }
        logger.debug("健康检查请求,url:{}", healthUrl);
        String resultJson = httpUtils.getRequest(healthUrl, null);
        logger.debug("健康检查返回结果:{}", resultJson);
        Map healthMap = JSON.parseObject(resultJson, Map.class);
        if (!"UP".equals(healthMap.get("status"))) {
            boolean flag = false;
            //兼容springboot版本
            if (healthMap.get("status") instanceof Map) {
                Map statusMap = (Map) healthMap.get("status");
                if ("UP".equals(statusMap.get("code"))) {
                    flag = true;
                }
            }
            if (!flag) {
                logger.debug(host + "的状态不是UP！");
                return false;
            }
        }
        return true;
    }

    private List<String> getIpList(List<String> hostList) {
        String[] hostArray = new String[hostList.size()];
        hostList.toArray(hostArray);
        ResponseVo<Map<String, List<String>>> ipMapVo = hostInfoService.queryIpByHost(hostArray);
        if (!ipMapVo.isSuccess() || ipMapVo.getData() == null) {
            return hostList;
        }

        List<String> resultList = new ArrayList<String>();

        Set<Map.Entry<String, List<String>>> entryseSet = ipMapVo.getData().entrySet();
        for (Map.Entry<String, List<String>> entry : entryseSet) {
            String hostName = entry.getKey();
            List<String> ipList = entry.getValue();
            if (ipList.size() == 1) {
                //ip只有一个，则直接取
                resultList.add(ipList.get(0));
            } else {
                //ip多个，优先取和hostName一致的ip，其次取和salt在一个网段内的
                for (String ip : ipList) {
                    if (hostName.equals(ip)) {
                        resultList.add(ip);
                        break;
                    }
                    //TODO ip暂时写死
                    if (ip.startsWith("10.0.11") ||
                            ip.startsWith("172.16.188") ||
                            ip.startsWith("172.16.189")) {
                        resultList.add(ip);
                        break;
                    }
                }

            }
        }
        return resultList;
    }

    /* (non-Javadoc)
     * @see IBuildRecordService#serverUp(java.lang.Long, java.lang.String)
     */
    @SuppressWarnings("rawtypes")
    @Override
    public boolean serverUp(Long appId, String host) {
        BuildModel buildModelParam = new BuildModel();
        buildModelParam.setAppId(appId);
        //TODO env
        BuildModel buildModel = buildModelMapper.selectOne(buildModelParam);
        Map modelMap = JSON.parseObject(buildModel.getModelJson(), Map.class);
        if (!modelMap.containsKey("port")) {
            throw new RuntimeException("应用端口不能为空");
        }
        Integer port = (Integer) modelMap.get("port");
        String eurekaUrl = (String) modelMap.get("eurekaUrl");
        String applicationName = (String) modelMap.get("applicationName");

        List<String> hostList = new ArrayList<String>();
        hostList.add(host);
        String ip = this.getIpList(hostList).get(0);

        JenkinsHttpUtils.deleteRequest(
                eurekaUrl + "/eureka/apps/" + applicationName + "/" + ip + ":" + port + "/status?value=UP");
        return true;
    }

}